/*
 * Copyright 2009 XueSong Guo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.webwheel.di.utils;

import cn.webwheel.di.*;
import static cn.webwheel.di.Desire.DefaultData;
import static cn.webwheel.di.Desire.DefaultName;
import cn.webwheel.di.engine.DefaultContainer;

import java.lang.reflect.Type;

/**
 * 拥有众多辅助方法的富DI容器。
 */
@SuppressWarnings("unchecked")
public class RichContainer extends DefaultContainer {

    public RichContainer(DefaultContainer... parents) {
        super(parents);
    }

    /**
     * 绑定一个类到一个实例，使用默认注入静态参数。
     * @param type 类型
     * @param obj 实例
     */
    public <T> void bind(Class<T> type, T obj) {
        bind((Type)type, DefaultName, obj);
    }

    /**
     * 绑定一个类型辅助类到一个实例，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param obj 实例
     */
    public <T> void bind(TypeLiteral<T> type, T obj) {
        bind(type.getType(), DefaultName, obj);
    }

    /**
     * 绑定一个类型标志到一个实例。
     * @param type 类型
     * @param name 注入静态参数
     * @param obj 实例
     */
    public <T> void bind(Class<T> type, String name, T obj) {
        bind((Type)type, name, obj);
    }

    /**
     * 绑定一个类型标志到一个实例。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param obj 实例
     */
    public <T> void bind(TypeLiteral<T> type, String name, T obj) {
        bind(type.getType(), name, obj);
    }

    private <T> void bind(Type type, String name, final T obj) {
        bind(new Key<T>(type, name), new Provider<T>() {
            public T get(Key<T> tKey, String data, NativeProvider<?> nativeProvider) {
                return obj;
            }
        });
    }

    /**
     * 显式绑定一个类型到本身，使用默认注入静态参数。
     * @param type 类型
     */
    public void bind(Class type) {
        bind((Type)type, DefaultName, type, false);
    }

    /**
     * 显式绑定一个类型到本身，使用默认注入静态参数。
     * @param type 类型
     * @param singleton 是否单例
     */
    public void bind(Class type, boolean singleton) {
        bind((Type)type, DefaultName, type, singleton);
    }

    /**
     * 绑定一个类型到实现类型，使用默认注入静态参数。
     * @param type 类型
     * @param impl 实现类型
     */
    public <T> void bind(Class<T> type, Class<? extends T> impl) {
        bind((Type)type, DefaultName, impl, false);
    }

    /**
     * 绑定一个类型到实现类型，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param impl 实现类型
     */
    public <T> void bind(TypeLiteral<T> type, Class<? extends T> impl) {
        bind(type.getType(), DefaultName, impl, false);
    }

    /**
     * 绑定一个类型到实现类型。
     * @param type 类型
     * @param name 注入静态参数
     * @param impl 实现类型
     */
    public <T> void bind(Class<T> type, String name, Class<? extends T> impl) {
        bind((Type)type, name, impl, false);
    }

    /**
     * 绑定一个类型到实现类型。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param impl 实现类型
     */
    public <T> void bind(TypeLiteral<T> type, String name, Class<? extends T> impl) {
        bind(type.getType(), name, impl, false);
    }

    /**
     * 绑定一个类型到实现类型，使用默认注入静态参数。
     * @param type 类型
     * @param impl 实现类型
     * @param singleton 是否单例
     */
    public <T> void bind(Class<T> type, Class<? extends T> impl, boolean singleton) {
        bind((Type)type, DefaultName, impl, singleton);
    }

    /**
     * 绑定一个类型到实现类型，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param impl 实现类型
     * @param singleton 是否单例
     */
    public <T> void bind(TypeLiteral<T> type, Class<? extends T> impl, boolean singleton) {
        bind(type.getType(), DefaultName, impl, singleton);
    }

    /**
     * 绑定一个类型到实现类型。
     * @param type 类型
     * @param name 注入静态参数
     * @param impl 实现类型
     * @param singleton 是否单例
     */
    public <T> void bind(Class<T> type, String name, Class<? extends T> impl, boolean singleton) {
        bind((Type)type, name, impl, singleton);
    }

    /**
     * 绑定一个类型到实现类型。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param impl 实现类型
     * @param singleton 是否单例
     */
    public <T> void bind(TypeLiteral<T> type, String name, Class<? extends T> impl, boolean singleton) {
        bind(type.getType(), name, impl, singleton);
    }

    private <T> void bind(Type type, String name, Class<? extends T> impl, boolean singleton) {
        final Key key = new Key(impl, name);
        Provider<T> provider;
        if (singleton) {
            provider = new Provider<T>() {
                volatile T inst;
                public T get(Key<T> tKey, String data, NativeProvider<?> nativeProvider) {
                    if(inst!=null) return inst;
                    synchronized (this) {
                        if(inst!=null) return inst;
                        return inst = (T) nativeProvider.get(key);
                    }
                }
            };
        } else {
            provider = new Provider<T>() {
                public T get(Key<T> tKey, String data, NativeProvider<?> nativeProvider) {
                    return (T) nativeProvider.get(key);
                }
            };
        }
        super.bind(new Key<T>(type, name), provider);
    }

    /**
     * 绑定一个类型到一个类型提供者，使用默认注入静态参数。
     * @param type 类型
     * @param provider 类型提供者
     */
    public <T> void bindProvider(Class<T> type, Provider<T> provider) {
        bindProvider((Type)type, provider);
    }

    /**
     * 绑定一个类型到一个类型提供者，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param provider 类型提供者
     */
    public <T> void bindProvider(TypeLiteral<T> type, Provider<T> provider) {
        bindProvider(type.getType(), provider);
    }

    private <T> void bindProvider(Type type, Provider<T> provider) {
        bindProvider(type, DefaultName, provider);
    }

    /**
     * 绑定一个类型到一个类型提供者。
     * @param type 类型
     * @param name 注入静态参数
     * @param provider 类型提供者
     */
    public <T> void bindProvider(Class<T> type, String name, Provider<T> provider) {
        bindProvider((Type)type, name, provider);
    }

    /**
     * 绑定一个类型到一个类型提供者。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param provider 类型提供者
     */
    public <T> void bindProvider(TypeLiteral<T> type, String name, Provider<T> provider) {
        bindProvider(type.getType(), name, provider);
    }

    private <T> void bindProvider(Type type, String name, Provider<T> provider) {
        bindProvider(type, name, provider, false);
    }

    /**
     * 绑定一个类型到一个实例提供者，使用默认注入静态参数。
     * @param type 类型
     * @param provider 实例提供者
     * @param singleton 是否单例
     */
    public <T> void bindProvider(Class<T> type, Provider<T> provider, boolean singleton) {
        bindProvider((Type)type, provider, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param provider 实例提供者
     * @param singleton 是否单例
     */
    public <T> void bindProvider(TypeLiteral<T> type, Provider<T> provider, boolean singleton) {
        bindProvider(type.getType(), provider, singleton);
    }

    private <T> void bindProvider(Type type, Provider<T> provider, boolean singleton) {
        bindProvider(type, DefaultName, provider, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者。
     * @param type 类型
     * @param name 注入静态参数
     * @param provider 实例提供者
     * @param singleton 是否单例
     */
    public <T> void bindProvider(Class<T> type, String name, Provider<T> provider, boolean singleton) {
        bindProvider((Type)type, name, provider, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param provider 实例提供者
     * @param singleton 是否单例
     */
    public <T> void bindProvider(TypeLiteral<T> type, String name, Provider<T> provider, boolean singleton) {
        bindProvider(type.getType(), name, provider, singleton);
    }

    private <T> void bindProvider(Type type, String name, final Provider<T> provider, boolean singleton) {
        Provider<T> provider2 = provider;
        if (singleton) {
            provider2 = new Provider<T>() {
                volatile T inst;
                public T get(Key<T> key, String data, NativeProvider<?> nativeProvider) {
                    if(inst!=null) return inst;
                    synchronized (this) {
                        if(inst!=null) return inst;
                        return inst = provider.get(key, data, nativeProvider);
                    }
                }
            };
        }
        bind(new Key<T>(type, name), provider2);
    }

    /**
     * 绑定一个类型到一个实例提供者类型，使用默认注入静态参数。
     * @param type 类型
     * @param providerCls 实例提供者类型
     */
    public <T> void bindProvider(Class<T> type, Class<? extends Provider<T>> providerCls) {
        bindProvider((Type)type, DefaultName, providerCls, false);
    }

    /**
     * 绑定一个类型到一个实例提供者类型，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param providerCls 实例提供者类型
     */
    public <T> void bindProvider(TypeLiteral<T> type, Class<? extends Provider<T>> providerCls) {
        bindProvider(type.getType(), DefaultName, providerCls, false);
    }

    /**
     * 绑定一个类型到一个实例提供者类型。
     * @param type 类型
     * @param name 注入静态参数
     * @param providerCls 实例提供者类型
     */
    public <T> void bindProvider(Class<T> type, String name, Class<? extends Provider<T>> providerCls) {
        bindProvider((Type)type, name, providerCls, false);
    }

    /**
     * 绑定一个类型到一个实例提供者类型。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param providerCls 实例提供者类型
     */
    public <T> void bindProvider(TypeLiteral<T> type, String name, Class<? extends Provider<T>> providerCls) {
        bindProvider(type.getType(), name, providerCls, false);
    }

    /**
     * 绑定一个类型到一个实例提供者类型，使用默认注入静态参数。
     * @param type 类型
     * @param providerCls 实例提供者类型
     * @param singleton 是否单例
     */
    public <T> void bindProvider(Class<T> type, Class<? extends Provider<T>> providerCls, boolean singleton) {
        bindProvider((Type)type, DefaultName, providerCls, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者类型，使用默认注入静态参数。
     * @param type 类型辅助类
     * @param providerCls 实例提供者类型
     * @param singleton 是否单例
     */
    public <T> void bindProvider(TypeLiteral<T> type, Class<? extends Provider<T>> providerCls, boolean singleton) {
        bindProvider(type.getType(), DefaultName, providerCls, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者类型。
     * @param type 类型
     * @param name 注入静态参数
     * @param providerCls 实例提供者类型
     * @param singleton 是否单例
     */
    public <T> void bindProvider(Class<T> type, String name, Class<? extends Provider<T>> providerCls, boolean singleton) {
        bindProvider((Type)type, name, providerCls, singleton);
    }

    /**
     * 绑定一个类型到一个实例提供者类型。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param providerCls 实例提供者类型
     * @param singleton 是否单例
     */
    public <T> void bindProvider(TypeLiteral<T> type, String name, Class<? extends Provider<T>> providerCls, boolean singleton) {
        bindProvider(type.getType(), name, providerCls, singleton);
    }

    private <T> void bindProvider(Type type, String name, final Class<? extends Provider<T>> providerCls, boolean singleton) {
        Provider<T> provider;
        if (singleton) {
            provider = new Provider<T>() {
                volatile T inst;
                public T get(Key<T> key, String data, NativeProvider<?> nativeProvider) {
                    if(inst!=null) return inst;
                    synchronized (this) {
                        if(inst!=null) return inst;
                        return inst = getInstance(providerCls).get(key, data, nativeProvider);
                    }
                }
            };
        } else {
            provider = new Provider<T>() {
                public T get(Key<T> key, String data, NativeProvider<?> nativeProvider) {
                    return getInstance(providerCls).get(key, data, nativeProvider);
                }
            };
        }
        bind(new Key<T>(type, name), provider);
    }

    /**
     * 获得类型实例，使用默认的注入动态参数。
     * @param key 类型标志
     * @return 实例对象
     */
    public <T> T getInstance(Key<T> key) {
        return super.getInstance(key, DefaultData);
    }

    /**
     * 获得类型实例，使用默认的注入静态参数和默认的注入动态参数。
     * @param type 类型
     * @return 实例对象
     */
    public <T> T getInstance(Class<T> type) {
        return super.getInstance(new Key<T>(type, DefaultName), DefaultData);
    }

    /**
     * 获得类型实例，使用默认的注入静态参数和默认的注入动态参数。
     * @param type 类型辅助类
     * @return 实例对象
     */
    public <T> T getInstance(TypeLiteral<T> type) {
        return super.getInstance(new Key<T>(type.getType(), DefaultName), DefaultData);
    }

    /**
     * 获得类型实例，使用默认的注入动态参数。
     * @param type 类型
     * @param name 注入静态参数
     * @return 实例对象
     */
    public <T> T getInstance(Class<T> type, String name) {
        return super.getInstance(new Key<T>(type, name), DefaultData);
    }

    /**
     * 获得类型实例，使用默认的注入动态参数。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @return 实例对象
     */
    public <T> T getInstance(TypeLiteral<T> type, String name) {
        return super.getInstance(new Key<T>(type.getType(), name), DefaultData);
    }

    /**
     * 获得类型实例，使用默认的注入动态参数。
     * @param type 类型
     * @param name 注入静态参数
     * @param data 动态注入数据
     * @return 实例对象
     */
    public <T> T getInstance(Class<T> type, String name, String data) {
        return super.getInstance(new Key<T>(type, name), data);
    }

    /**
     * 获得类型实例，使用默认的注入动态参数。
     * @param type 类型辅助类
     * @param name 注入静态参数
     * @param data 动态注入数据
     * @return 实例对象
     */
    public <T> T getInstance(TypeLiteral<T> type, String name, String data) {
        return super.getInstance(new Key<T>(type.getType(), name), data);
    }

    /**
     * 获得一个类型的实例提供者。
     * @param type 类型
     * @return 实例提供者
     */
    public <T> Provider<? extends T> getProvider(Class<T> type) {
        return super.getProvider(new Key<T>(type, DefaultName));
    }

    /**
     * 获得一个类型的实例提供者。
     * @param type 类型
     * @return 实例提供者
     */
    public <T> Provider<? extends T> getProvider(TypeLiteral<T> type) {
        return super.getProvider(new Key<T>(type.getType(), DefaultName));
    }

    /**
     * 获得一个类型的实例提供者。
     * @param type 类型
     * @param name 注入静态参数
     * @return 实例提供者
     */
    public <T> Provider<? extends T> getProvider(Class<T> type, String name) {
        return super.getProvider(new Key<T>(type, name));
    }

    /**
     * 获得一个类型的实例提供者。
     * @param type 类型
     * @param name 注入静态参数
     * @return 实例提供者
     */
    public <T> Provider<? extends T> getProvider(TypeLiteral<T> type, String name) {
        return super.getProvider(new Key<T>(type.getType(), name));
    }

    /**
     * 获取实例获取器
     * @param cls 类型
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(Class<T> cls) {
        return super.createFetcher(new Key<T>(cls, Desire.DefaultName), Desire.DefaultData);
    }

    /**
     * 获取实例获取器
     * @param cls 类型
     * @param name 注入静态参数
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(Class<T> cls, String name) {
        return super.createFetcher(new Key<T>(cls, name), Desire.DefaultData);
    }

    /**
     * 获取实例获取器
     * @param cls 类型
     * @param name 注入静态参数
     * @param data 动态参数
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(Class<T> cls, String name, String data) {
        return super.createFetcher(new Key<T>(cls, name), data);
    }

    /**
     * 获取实例获取器
     * @param type 类型
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(TypeLiteral<T> type) {
        return super.createFetcher(new Key<T>(type.getType(), Desire.DefaultName), Desire.DefaultData);
    }

    /**
     * 获取实例获取器
     * @param type 类型
     * @param name 注入静态参数
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(TypeLiteral<T> type, String name) {
        return super.createFetcher(new Key<T>(type.getType(), name), Desire.DefaultData);
    }

    /**
     * 获取实例获取器
     * @param type 类型
     * @param name 注入静态参数
     * @param data 动态参数
     * @return 实例获取器
     */
    public <T> Fetcher<T> createFetcher(TypeLiteral<T> type, String name, String data) {
        return super.createFetcher(new Key<T>(type.getType(), name), data);
    }
}
